package com.JustTest;

import com.alibaba.fastjson.JSON;
import edu.emory.mathcs.backport.java.util.Collections;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import static org.apache.zookeeper.Watcher.Event.EventType.NodeCreated;

/**
 * Created by mike on 18/5/18.
 */
public class DistributeLock implements Watcher {
    private int sessionTimeout = 3000;
    private String root = "locks";
    private CountDownLatch latch = new CountDownLatch(1);
    private String lockName;
    private ZooKeeper zk;
    private String split = "_split_";
    private List<String> lockNodes = new ArrayList<>();
    private String myNode = new String();
    private String lockNode = new String();

    public DistributeLock(String connectionString,String lockName) {
        try {
            this.lockName = lockName;
            zk = new ZooKeeper(connectionString, sessionTimeout, this);
            if (zk.exists("/"+root, null) == null) {
                zk.create("/"+root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (KeeperException e1) {

        } catch (InterruptedException e2) {

        }
    }

    @Override
    public void process(WatchedEvent watchedEvent) {
        if(null!=watchedEvent){
//            System.out.println("创建节点成功.......");
            latch.countDown();
        }
    }

    public boolean lock(){
        try {
            if(lockName.contains(split)){
                System.out.println("不能包含:"+split);
                throw new Exception();
            }
            latch.await();
            myNode = zk.create("/"+root+"/"+lockName+split,new byte[0],ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
            System.out.println("当前锁对象节点:"+myNode);
            if(tryLock()){
                return true;
            }
            waitForLock();
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }catch (KeeperException e1){

        }catch (Exception e0){

        }
        return true;
    }

    public boolean tryLock(){
        try {
            List<String> nodes = zk.getChildren("/"+root,null);
            for(String node:nodes){
                if(node.split(split)[0].equals(lockName)){
                    lockNodes.add("/"+root+"/"+node);
                }
            }
            Collections.sort(lockNodes);
//            System.out.println("锁队列:"+JSON.toJSONString(lockNodes));
            if(lockNodes.get(0).equals(myNode)){
                System.out.println("获取锁成功："+myNode);
                return true;
            }

            lockNode = lockNodes.get(Collections.binarySearch(lockNodes,myNode)-1);
//            System.out.println("监控："+lockNode);
            zk.getData(lockNode,this,null);
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    public void waitForLock(){
        try {
            latch = new CountDownLatch(1);
            System.out.println(myNode+"进入等待");
            latch.await();
            System.out.println(myNode+"完成等待获取锁");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void unlock(){
        try {
            zk.delete(myNode,-1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    };
}
